package com.simple.dsrouter.strategy.impl;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import com.simple.dsrouter.config.DataSourceConfig;
import com.simple.dsrouter.holder.DataSourceContextHolder;

/**
 * 实现权重轮询路由策略
 */
public class RobinMasterSlaveRouteStrategy extends ModMasterSlaveRouteStrategy {

	@Override
	protected void routeSlaveDataSource(List<DataSourceConfig> dataSources, int modKey, Object target, Method method, Object[] args) {
		List<Server> serverList = new ArrayList<Server>();
		for (DataSourceConfig dataSource : dataSources) {
			Server server = new Server(dataSource.getDatasourceId(), dataSource.getWeight());
			serverList.add(server);
		}
		String slaveDataSourceKey = getBestServer(serverList).getDatasourceId();
		if (logger.isDebugEnabled()) {
			logger.debug("read method: " + method.getName() + " --> reslove routing parameter mod value: " + modKey + " --> routing to slave datasource: " + slaveDataSourceKey);
		}
		DataSourceContextHolder.setCurrentDataSourceKey(slaveDataSourceKey);
	}

	private Server getBestServer(List<Server> serverList) {
		Server server = null;
		Server best = null;
		int total = 0;
		for (int i = 0, len = serverList.size(); i < len; i++) {
			// 当前服务器对象
			server = serverList.get(i);
			// 当前服务器已宕机，排除
			if (server.down) {
				continue;
			}
			server.currentWeight += server.effectiveWeight;
			total += server.effectiveWeight;
			if (server.effectiveWeight < server.weight) {
				server.effectiveWeight++;
			}
			if (best == null || server.currentWeight > best.currentWeight) {
				best = server;
			}
		}
		if (best == null) {
			return null;
		}
		best.currentWeight -= total;
		return best;
	}

	class Server {
		/**
		 * 权重
		 */
		private int weight;
		/**
		 * 实际权重
		 */
		private int effectiveWeight;
		/**
		 * 当前权重
		 */
		private int currentWeight;
		/**
		 * 数据源id
		 */
		private String datasourceId;

		private boolean down = false;

		public Server(String datasourceId, int weight) {
			super();
			this.datasourceId = datasourceId;
			this.weight = weight;
			this.effectiveWeight = this.weight;
			this.currentWeight = 0;
			if (this.weight < 0) {
				this.down = true;
			} else {
				this.down = false;
			}
		}

		public int getWeight() {
			return weight;
		}

		public void setWeight(int weight) {
			this.weight = weight;
		}

		public int getEffectiveWeight() {
			return effectiveWeight;
		}

		public void setEffectiveWeight(int effectiveWeight) {
			this.effectiveWeight = effectiveWeight;
		}

		public int getCurrentWeight() {
			return currentWeight;
		}

		public void setCurrentWeight(int currentWeight) {
			this.currentWeight = currentWeight;
		}

		public boolean isDown() {
			return down;
		}

		public void setDown(boolean down) {
			this.down = down;
		}

		public String getDatasourceId() {
			return datasourceId;
		}

		public void setDatasourceId(String datasourceId) {
			this.datasourceId = datasourceId;
		}
	}
}
